package cn.melonlib.cores.jpa.model.bean.collections;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.melonlib.cores.commons.model.entity.ApiEntity;
import cn.melonlib.cores.commons.model.entity.IEntity;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.cglib.core.ReflectUtils;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Getter
public class EntityMap<V,O extends IEntity<? extends Serializable>> extends JsonMap<V> {
    private final Logger logger= LogManager.getLogger();

    private final O entity;

    private final ObjectMapper mapper=new ObjectMapper();

    private final Set<String> ignores=new HashSet<>();
    {
        ignores.add("handler");
        ignores.add("hibernateLazyInitializer");
        ignores.add("allChildren");
        ignores.add("children");
//        ignores.add("parents");
        ignores.add("top");
    }
    /**
     * 构造函数，传入实体对象，将所有属性转换为keyvalue的形式保存入Map
     * @param entity hibernate实体对象
     */
    public EntityMap(O entity) {
        this(entity, Collections.emptySet());
    }

    /**
     * 造函数，传入实体对象，将所有属性转换为keyvalue的形式保存入Map
     * @param entity 实体对象
     * @param ignores 需要忽略的对象
     */
    public EntityMap(O entity,Set<String> ignores) {
        this.entity=entity;
        this.ignores.addAll(ignores);
        Assert.notNull(entity,"传入的对象不能为空");
        this.put("id", (V) entity.getId());
        Arrays.stream(ReflectUtils.getBeanGetters(entity.getClass()))
                .filter(this::filterMethod)
                .forEach(this::pushValues);
    }

    private boolean filterMethod(PropertyDescriptor propertyDescriptor) {
        return  !this.ignores.contains(propertyDescriptor.getName());
    }

    private void pushValues(PropertyDescriptor pd) {
        String n=pd.getName();
        try {
            Object v=pd.getReadMethod().invoke(entity);
            if(v instanceof Collection) {
                Object o=((Collection) v).stream().map(this::pushValue).collect(Collectors.toList());
                if(((Collection) v).size()!=0){
                    if(((Collection) v).stream().findAny().get() instanceof ApiEntity){
                        this.put(n,(V)o);
                    }else{
                        this.put(n+"_list",(V)o);
                    }
                }
            }else if(v instanceof IEntity) {
                if(v instanceof ApiEntity){
                    this.put(n,(V)new EntityMap<Object,O>((O) v));
                }else{
                    this.put(n+"_id",(V)((IEntity<Serializable>) v).getId());
                    Arrays.stream(ReflectUtils.getBeanGetters(pd.getPropertyType())).forEach(getter->{
                        try {
                            switch (getter.getName()) {
                                case "code": {
                                    this.put(n + "_code", (V)getter.getReadMethod().invoke(v));
                                    return;
                                }
                                case "name": {
                                    this.put(n + "_name", (V) getter.getReadMethod().invoke(v));
                                    return;
                                }
                                case "label": {
                                    this.put(n + "_label", (V) getter.getReadMethod().invoke(v));
                                    return;
                                }
                            }
                        }catch(Exception e){
                        }
                    });
                }
            }else if(v instanceof Enum){
                this.put(n+"_label",BeanUtil.getProperty(v,"label"));
                this.put(n, (V) ((Enum) v).name());
            }else {
                this.put(n,(V)v);
            }

        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            logger.warn("获取对象值失败："+e.getMessage());
        }
    }

    private Object pushValue(Object v) {
        if(v instanceof IEntity) {
            if(v instanceof ApiEntity){
                return new EntityMap<>((IEntity) v);
            }else{
                return ((IEntity<Serializable>) v).getId();
            }
        }
        if(v instanceof Collection) {
            return ((Collection) v).stream().map(this::pushValue).collect(Collectors.toList());
        }
        return v;
    }

    public Logger getLogger() {
        return logger;
    }

    /**
     * 获取原始的hibernate数据对象
     * @return
     */
    public O getEntity() {
        return entity;
    }

    /**
     * 添加一个属性
     */
    public EntityMap<V, O> append(String key, V value) {
        this.put(key, value);
        return this;
    }
    public EntityMap<V, O> appendPath(String key, String path, Function<V, V> trans) {
        try {
            V o=(V)BeanUtil.getProperty(entity, path);
            if(o instanceof IEntity) {
                this.put(key, trans.apply((V)new EntityMap<>((IEntity) o)));
            }else {
                this.put(key, trans.apply(o)) ;
            }

        }catch(Exception e) {
            logger.warn("没有招到对应属性");
        }
        return this;
    }

    public EntityMap<V, O> appendPath(String key, String path) {
        try {
            V o=(V)BeanUtil.getProperty(entity, path);
            if(o instanceof IEntity) {
                this.put(key, (V)new EntityMap<>((IEntity) o));
            }else {
                this.put(key, o) ;
            }

        }catch(Exception e) {
            logger.warn("没有招到对应属性");
        }
        return this;
    }

    public EntityMap<V, O> appendJsonMap(String key, String path, String jsonPath){
        try {
            String values=(String)BeanUtil.getProperty(entity, path);
            if(StringUtils.isNoneBlank(values)) {
                org.hibernate.mapping.Map v=mapper.readValue(values, org.hibernate.mapping.Map.class);
                this.put(key,(V)BeanUtil.getProperty(v, jsonPath)) ;
            }

        }catch(Exception e) {
            logger.warn("没有招到对应属性");
        }
        return this;
    }

    public EntityMap<V, O> appendDate(String key, String path, String pattern){
        try {
            Long ts=(Long)BeanUtil.getProperty(entity, path);
            Date date=new Date(ts);
            this.put(key, (V) DateFormatUtils.format(date, pattern)) ;
        }catch(Exception e) {
            logger.warn("没有招到对应属性");
        }
        return this;
    }

    public EntityMap<V, O> clearEmpty() {
        keySet().stream().collect(Collectors.toList()).forEach(key->{
            Object val=get(key);
            if(val==null) {
                remove(key);
            }
            if((val instanceof CharSequence)&&"".equals(val.toString())) {
                remove(key);
            }
        });
        return this;
    }
}
